<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Vue Barrage</title>
    <style>
        html, body {
            margin: 0;
        }
        [v-cloak] {
            display: none;
        }
        #main {
            position: relative;
            max-width: 100%;
            min-height: 100vh;
            font-size: 16px;
            overflow: hidden;
            background: #19233C;
            user-select: none;
        }
        #main * {
            outline: none;
        }
        .barrageSend {
            position: absolute;
            width: 200px;
            height: 200px;
            bottom: 4em;
            right: 4em;
            padding: 1em;
            border-radius: 50%;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            background: linear-gradient(to top, #F3E7E9 0%, #E3EEFF 99%, #E3EEFF 100%);
            box-shadow: 0 0 1em #E3EEFF;
            opacity: 0.5;
            transition: 0.5s;
            transform: translateX(50%);
        }
        .barrageSend:hover {
            width: 500px;
            height: 300px;
            border-radius: 3em;
            opacity: 1;
            transform: translateX(0);
        }
        .barrageSend > * {
            margin: 1em;
            display: block;
            transition: 0.8s;
        }
        .barrageSend > div{
            width: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .barrageSend > div:nth-of-type(1) {
            height: 2em;
        }
        .barrageSend > div:nth-of-type(2) {
            height: 3.2em;
        }
        .barrageSend > div:nth-of-type(3) {
            height: 1.2em;
            justify-content: space-evenly;
        }
        .barrageSend > div:nth-of-type(3) > div,
        .barrageSend > div:nth-of-type(3) > div > div,
        .barrageSend .sizeBox {
            display: flex;
            align-items: center;
        }
        .barrageSend:not(:hover) > div, .barrageSend:not(:hover) > button {
            height: 0;
            margin: 0;
            padding: 0;
            opacity: 0;
        }
        .barrageSend .title{
            margin-right: 0.5em;
        }
        .barrageSend .title.fix{
            width: 4.5em;
            text-align: right;
        }
        .barrageSend .sizeBox span {
            margin-right: 0.5rem;
            padding: 0.2rem 1rem;
            line-height: 100%;
            font-size: 1em;
            display: inline-block;
            transition: 0.3s;
        }
        .barrageSend .sizeBox span.active {
            background: #CCC;
            border-radius: 0.25rem;
        }
        .barrageSend .sizeBox span.small {
            font-size: 0.8em;
        }
        .barrageSend .sizeBox span.big {
            font-size: 1.3em;
        }
        .barrageSend .colorBox {
            position: relative;
            padding: 0.8em 1.5em;
            border-radius: 0.5em;
            background: #FFF;
            box-shadow: 0 0 0.25em #888, inset 0 0 0.25em #FFF;
            cursor: pointer;
            transition: background 0.2s;
        }
        .barrageSend .colorBox > div {
            position: absolute;
            width: 220px;
            height: 55px;
            bottom: 125%;
            right: -100%;
            padding: 15px;
            border-radius: 10px;
            background: rgba(50, 55, 80, 0.74);
            display: flex;
            justify-content: space-around;
            align-items: center;
            align-content: center;
            flex-wrap: wrap;
            overflow: hidden;
            transition: 0.3s;
        }
        .barrageSend .colorBox:not(:hover) > div {
            height: 0;
            opacity: 0;
            visibility: hidden;
        }
        .barrageSend .colorBox span {
            width: 20px;
            height: 20px;
            margin: 8px;
            border-radius: 4px;
            background: #FFF;
            border: 1px solid rgba(0,0,0,.3);
            box-sizing: border-box;
            display: inline-block;
        }
        .barrageSend .colorBox span.active {
            box-shadow: 0 0 1px 1px #FFF;
        }
        .barrageSend input[type="text"], .barrageSend textarea {
            min-width: 75%;
            padding: 0.5em;
            border-radius: 0.5em;
            border: none;
            resize: none;
        }
        .barrageSend button {
            padding: 0.5em 2em;
            border: none;
            border-radius: 1em;
            color: white;
            background: #6495ed;
            box-shadow: 0 0 0.5em #88a9e7;
            transition: all 0.8s, background 0.2s;
        }
        .barrageSend button:active {
            background: #4779d6;
        }
        .barrageBox {
            position: fixed;
            max-width: 80%;
            max-height: 60%;
            bottom: 0;
            margin: 2em;
            overflow: auto;
        }
        .barrageBox::-webkit-scrollbar {
            display: none;
        }
        .barrageBox > p {
            margin: 1.2em 0;
            word-break: break-all;
            color: white;
        }
        .barrageBox > p > span:first-of-type {
            color: skyblue;
        }
        .barrageScreen > span {
            position: fixed;
            white-space: nowrap;
            line-height: 100%;
            font-size: 1em;
            color: white;
        }
        .barrageScreen > span.small {
            font-size: 0.8em;
        }
        .barrageScreen > span.big {
            font-size: 1.3em;
        }
    </style>
</head>
<body>
    <div id="main" v-cloak>
        <barrage-screen></barrage-screen>
        <barrage-box></barrage-box>
        <barrage-send></barrage-send>
    </div>
    <script src="vue.js"></script>
    <script>
        var vm = new Vue({
            el: "#main",
            data: {
                config: {
                    size: ["small", "normal", "big"],
                    color: [
                        "#FE0302", "#FF9A02", "#FFFF00", "#A0EE00", "#00CD00", "#019899",
                        "#4266BE", "#89D5FF", "#CC0273", "#222222", "#9B9B9B", "#FFFFFF"
                    ]
                },
                data: (function () {
                    var sData = window.localStorage.getItem("VueBarrage-data");
                    return sData && JSON.parse(sData) || [
                        { time: 1, name: "User_1", content: "Small barrage content 1", size: "small", color: "red" },
                        { time: 2, name: "User_2", content: "Normal barrage content 2", size: "normal", color: "blue" },
                        { time: 3, name: "User_3", content: "Big barrage content 3", size: "big", color: "green" }
                    ];
                })()
            },
            methods: {
                _comNode (name) {
                    var res = null;
                    name = name[0].toLowerCase() + name.substr(1).replace(/[A-Z]/g, v => "-" + v.toLowerCase());
                    this.$children.some(com => {
                        if (com.$options._componentTag == name) { res = com; return true }
                    })
                    return res;
                }
            },
            components: {
                barrageScreen: {
                    template: 
                    `<transition-group tag="div" class="barrageScreen" @before-enter="beforeEnter" @enter="enter" appear>
                        <span v-for="item in screenData" :key="item.time" :class="[item.size, 'time_' + item.time]" :style="{ color: item.color }">{{ item.content }}</span>
                    </transition-group>`,
                    data () {
                        return {
                            screenData: this.$parent.data.concat(),
                            screenIndex: 0
                        }
                    },
                    methods: {
                        beforeEnter (el) {
                            var t = this, data = t.screenData, index = t.screenIndex;
                            if (index < data.length - 1) t.screenIndex++;
                            else index = data.length - 1;
                            el.style.top = 24 * index % document.body.offsetWidth + "px";
                            el.style.left = "100%";
                        },
                        enter (el) {
                            el.offsetLeft;
                            el.style.left = -(el.offsetWidth + 10) + "px";
                            el.style.transition = "10s linear";
                            setTimeout(() => {
                                var time = 0;
                                el.classList.forEach(v => {
                                    if (v.indexOf("time_") == 0) time = parseInt(v.substr(5));
                                });
                                this.screenData.some((v, i) => {
                                    if (time == v.time) { this.screenData.splice(i, 1); return true }
                                });
                                el.style.transition = "none";
                            }, 10000);
                        }
                    }
                },
                barrageBox: {
                    template: 
                    `<transition-group tag="div" class="barrageBox" @before-enter="beforeEnter" @enter="enter" appear>
                        <p v-for="item in $parent.data" :key="item.time"><span>{{ item.name }}</span> : <span :style="{ color: item.color }">{{ item.content }}</span></p>
                    </transition-group>`,
                    methods: {
                        beforeEnter (el) {
                            el.style.opacity = 0;
                            el.style.transform = "translateX(-100%)";
                        },
                        enter (el) {
                            el.offsetLeft;
                            el.style.opacity = 1;
                            el.style.transform = "translateX(0)";
                            el.style.transition = "0.6s cubic-bezier(0.3, 0, 0.32, 1.4)";
                            el.parentElement.scrollTop = el.parentElement.scrollHeight;
                        },
                    }
                },
                barrageSend: {
                    template: 
                    `<div class="barrageSend">
                        <p>发条友善的弹幕吧</p>
                        <div><span class="title fix">昵称:</span><input type="text" v-model="sendData.name" @input="pattern(1)"></div>
                        <div><span class="title fix">内容:</span><textarea v-model="sendData.content" @input="pattern(2)"></textarea></div>
                        <div>
                            <div>
                                <span class="title">大小:</span>
                                <span class="sizeBox">
                                    <span v-for="s in $parent.config.size" :class="['size', s, { active: s == sendData.size }]" @click="sizeChange(s)">{{ s[0].toUpperCase() }}</span>
                                </span>
                            </div>
                            <div>
                                <span class="title">颜色:</span>
                                <span class="colorBox" :style="{ background: sendData.color }">
                                    <div>
                                        <span v-for="c in $parent.config.color" :class="{ active: c == sendData.color }" :style="{ background: c }" @click="colorChange(c)"></span>
                                    </div>
                                </span>
                            </div>
                        </div>
                        <button @click="send">发送</button>
                    </div>`,
                    data () {
                        return {
                            sendData: { name: "", content: "", size: "normal", color: "#FFFFFF" }
                        }
                    },
                    methods: {
                        pattern (t) {
                            var s = this.sendData;
                            if (t == 1 && s.name.length > 12) s.name = s.name.substr(0, 12);
                            else if (t == 2 && s.content.length > 50) s.content = s.content.substr(0, 50);
                        },
                        sizeChange (v) {
                            this.sendData.size = v;
                        },
                        colorChange (v) {
                            this.sendData.color = v;
                        },
                        send () {
                            var t = this, send = t.sendData;
                            if (send.name != "" && send.content != "") {
                                var { name, content, size, color } = send,
                                    barrage = { time: new Date().getTime(), name, content, size, color };
                                t.$parent._comNode("barrageScreen").screenData.push(barrage);
                                t.$parent.data.push(barrage);
                                t.$parent.data.sort((a, b) => a.time - b.time);
                                window.localStorage.setItem("VueBarrage-data", JSON.stringify(t.$parent.data));
                            }
                        }
                    }
                }
            },
            created () {
                var t = this;
                t.data.sort((a, b) => a.time - b.time);
                var randomSend = () => {
                    setTimeout(() => {
                        var id = Math.floor(Math.random() * 99999999 ) + 1,
                            barrage = {
                                time: new Date().getTime(),
                                name: `User_${id}`,
                                content: `我是${id}，这是第${t.data.length}条弹幕`,
                                size: t.config.size[Math.floor(Math.random() * 3)],
                                color: t.config.color[Math.floor(Math.random() * 12)]
                            };
                        t.data.push(barrage); t._comNode("barrageScreen").screenData.push(barrage);
                        t.data.sort((a, b) => a.time - b.time);
                        window.localStorage.setItem("VueBarrage-data", JSON.stringify(t.data));
                        randomSend();
                    }, Math.floor(Math.random() * (10000 - 250 + 1)) + 250);
                };
                randomSend();
            }
        });
    </script>
</body>
</html>